#include <dblib.h>
#include <memory>
#include <stdafx.h>
#include <vector>
#include <algorithm>
#include <functional>
#include <records.h>
#include <params.h>
#include <constants.h>
#include <stdexcept>

using namespace std;

#ifdef __cplusplus
extern "C" {
#endif

	RETCODE __declspec(dllexport) xp_dirscan(SRV_PROC *srvproc);

#ifdef __cplusplus
}
#endif

int ONE = 1;

BOOL nodots(std::string name) {
	return (name.compare(".") != 0 && name.compare("..") != 0);
}

BOOL endslash(std::string str) {
	return (str.length() >= 1 && str.substr(str.length() - 1, 1).compare("\\") == 0);
}

BOOL GetFileSystemObject (HANDLE &h, std::string dirspec, std::string mask, WIN32_FIND_DATA &FindFileData, FSOInfo &f, int level) {
	BOOL good = TRUE;
	auto_ptr<std::string> p (new std::string);
	*p = dirspec;
	p->append(mask);
	if (h == INVALID_HANDLE_VALUE) {
		h = FindFirstFile(p->c_str(), &FindFileData);
		if (h == INVALID_HANDLE_VALUE) {
			good = FALSE;
		}
	} else {
		good = FindNextFile(h, &FindFileData);
	}
	if (good) {
        f.fullpath = dirspec;
		f.name = FindFileData.cFileName;
		f.level = level;
		if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
			f.fullpath.append(FindFileData.cFileName);
			if (!endslash(f.fullpath))
				f.fullpath.append("\\");
			f.obj_type = 'D';
			f.size = 0;
//			f.create.dtdays = FindFileData.ftCreationTime.dwHighDateTime;
//			f.create.dttime = FindFileData.ftCreationTime.dwLowDateTime;
		} else {
			if (!endslash(f.fullpath))
				f.fullpath.append("\\");
			f.obj_type = 'F';
			f.filecount = 1;
			f.size = FindFileData.nFileSizeHigh * MAXDWORD + FindFileData.nFileSizeLow;
//			f.create.dtdays = FindFileData.ftCreationTime.dwHighDateTime;
//			f.create.dttime = FindFileData.ftCreationTime.dwLowDateTime;
		}
	}
	p->resize(0);
	p->swap(*p);
	return good;
}

FSOSum GetAll (std::string path, std::string mask, int level, vector<FSOInfo> &FileSystem) {
	
	FSOSum fs;
	fs.count = 0;
	fs.size = 0;

	FSOInfo fi;
	fi.size = 0;
	fi.filecount = 0;
	fi.level = level;
	fi.obj_type = 'D';
	fi.dirnum = 0;

	if (!endslash(path))
		path.append("\\");
	fi.fullpath = path;
	FileSystem.push_back(fi);
	ULONG dir_num = (int)FileSystem.size() - 1;

	WIN32_FIND_DATA FindFileData;
	HANDLE hFind = INVALID_HANDLE_VALUE;
	fi.level = level;
    while (GetFileSystemObject(hFind, path, mask, FindFileData, fi, level)) {
		if (fi.obj_type == 'F') {
			fs.count++;
			fs.size += fi.size;
			fi.dirnum = dir_num;
			if (!endslash(fi.fullpath))
				path.append("\\");
			FileSystem.push_back(fi);
		} 
	}
	FindClose(hFind);
	hFind = INVALID_HANDLE_VALUE;
	while (GetFileSystemObject(hFind, path, "*.*", FindFileData, fi, level)) {
		if (fi.obj_type == 'D' && nodots(fi.name)) {
			if (!endslash(fi.fullpath))
				fi.fullpath.append("\\");
			FSOSum b;
			b.count = 0;
			b.size = 0;
			if ((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) == 0) {
				b = GetAll(fi.fullpath, mask, level + 1, FileSystem);
			}
			fs.size += b.size;
			fs.count += b.count;
			fi.size = fs.size;
			fi.filecount = fs.count;
		}
	}
	FileSystem[dir_num].size = fs.size;
	FileSystem[dir_num].filecount = fs.count;
	FileSystem[dir_num].dirnum = dir_num;

	FindClose(hFind);
	return (fs);
}

void ForceVectors()
{
	FSOInfo *pClass = new FSOInfo[2];
}

static void (*pFunc)() = ForceVectors;

RETCODE __declspec(dllexport) xp_dirscan(SRV_PROC *srvproc)
{
	params *p1 = new params();
	params *p2 = new params();
	RETCODE rc = XP_NOERROR;
	ULONG rowcount = 0;
	vector<FSOInfo> FileSystem;
	try
	{
		int numparams = params::getparamcount(srvproc);
		if (numparams != 2) {
			Dblib::printerror(srvproc, USAGE_DIRSCAN);
			rc = XP_ERROR;
		}
		if (rc == XP_NOERROR) {
			params::getparam(srvproc, 1, p1);
			params::getparam(srvproc, 2, p2);
			ONE = 1;
			srv_describe(srvproc, 1, "RowNum", SRV_NULLTERM, SRVINT4, 0, SRVINT4, 0, NULL);
			srv_describe(srvproc, 2, "Level", SRV_NULLTERM, SRVINT4, 0, SRVINT4, 0, NULL);
			srv_describe(srvproc, 3, "IsDir", SRV_NULLTERM, SRVCHAR, 1, SRVCHAR, 1, NULL);
			srv_describe(srvproc, 4, "FullPath", SRV_NULLTERM, SRVBIGVARCHAR, 2048, SRVBIGVARCHAR, 0, NULL);
			srv_describe(srvproc, 5, "FileName", SRV_NULLTERM, SRVBIGVARCHAR, 256, SRVBIGVARCHAR, 0, NULL);
			srv_describe(srvproc, 6, "Size" , SRV_NULLTERM, SRVNUMERIC, 19, SRVNUMERIC, 19, NULL);
			srv_describe(srvproc, 7, "FileCount", SRV_NULLTERM, SRVINT4, 0, SRVINT4, 0, NULL);
//			srv_describe(srvproc, 8, "CreateDate", SRV_NULLTERM, SRVDATETIME, 0, SRVDATETIME, 0, NULL);
			std::string path = (char *)p1->cdata;
			std::string mask = (char *)p2->cdata;
			FileSystem.reserve(1024);
			FSOInfo fi;
			FileSystem.empty();
			if (!endslash(path))
				path.append("\\");
			fi.fullpath = path;
			FSOSum fs = GetAll(path, mask, 1, FileSystem);
			mask.resize(0);
			mask.swap(mask);
			path.resize(0);
			path.swap(path);

			DBNUMERIC s1;
			s1.precision = DEFAULTPRECISION;
			s1.scale = 0;
			s1.sign = 1;
			srv_bzero(s1.val, 16);
			vector<FSOInfo>::iterator start, end;
			start = FileSystem.begin();
			end = FileSystem.end();
			for (; start != end; start++) {
				rowcount++;
				if (start->obj_type == 'F') {
					srv_setcoldata(srvproc, 1, (BYTE *)&rowcount);
					srv_setcoldata(srvproc, 2, (BYTE *)&start->level);
					srv_setcoldata(srvproc, 3, "N");
					srv_setcoldata(srvproc, 4, (char *)start->fullpath.c_str());
					srv_setcollen(srvproc, 4, (int)start->fullpath.length());
					srv_setcoldata(srvproc, 5, (char *)start->name.c_str());
					srv_setcollen(srvproc, 5, (int)start->name.length());
					memcpy(s1.val, (BYTE *)&start->size, 8);
					srv_setcoldata(srvproc, 6, (BYTE *)&s1);
					srv_setcoldata(srvproc, 7, (BYTE *)&ONE);
//					srv_setcoldata(srvproc, 8, (BYTE *)&start->create);
					srv_sendrow(srvproc);
					start->fullpath.resize(0);
					start->fullpath.swap(start->fullpath);
					start->name.resize(0);
					start->name.swap(start->name);
				} else {			
					srv_setcoldata(srvproc, 1, (BYTE *)&rowcount);
					srv_setcoldata(srvproc, 2, (BYTE *)&start->level);
					srv_setcoldata(srvproc, 3, "Y");
					srv_setcoldata(srvproc, 4, (char *)start->fullpath.c_str());
					srv_setcollen(srvproc, 4, (int)start->fullpath.length());
					srv_setcoldata(srvproc, 5, " ");
					srv_setcollen(srvproc, 5, ONE);
					memcpy(s1.val, (BYTE *)&start->size, 8);
					srv_setcoldata(srvproc, 6, (BYTE *)&s1);
					srv_setcoldata(srvproc, 7, (BYTE *)&start->filecount);
//					srv_setcoldata(srvproc, 8, (BYTE *)&start->create);
					srv_sendrow(srvproc);
					start->fullpath.resize(0);
					start->fullpath.swap(start->fullpath);
					start->name.resize(0);
					start->name.swap(start->name);
				}
			}
		}
	} catch (std::exception ex) {
		Dblib::printerror(srvproc, ERR_DIRSCAN_EXCEPTION);
		rc = XP_ERROR;
	}

	if (rc == XP_NOERROR)
		srv_senddone(srvproc, SRV_DONE_MORE | SRV_DONE_COUNT, (DBUSMALLINT)0, (DBINT)rowcount);
	else
		srv_senddone(srvproc, SRV_DONE_MORE | SRV_DONE_COUNT | SRV_DONE_ERROR, (DBUSMALLINT)0, (DBINT)0);
	FileSystem.erase(FileSystem.begin(), FileSystem.end());
	FileSystem.resize(0);
	std::vector<FSOInfo>().swap(FileSystem);
	if (p1 != NULL)
		delete p1;
	p1 = NULL;
	if (p2 != NULL)
		delete p2;
	p2 = NULL;
	return XP_NOERROR;
}